xquery version "1.0";

(:~
	Search Engine file. The main query generator and the filtering of keywords
	are stored here. The fields of keywords are passed to the filter to tokenize
	per keyword and phrase. Removes the keywords that are not relevant. Then pass
	all the keywords to the query generator to build the search query.
:)


declare namespace MedEx="http://localhost:8080/exist/MedEx";
declare namespace system="http://exist-db.org/xquery/system";
import module namespace util="http://exist-db.org/xquery/util";
import module namespace request="http://exist-db.org/xquery/request";
import module namespace session="http://exist-db.org/xquery/session";
import module namespace xdb="http://exist-db.org/xquery/xmldb";

declare option exist:serialize "method=xml media-type=text/xml omit-xml-declaration=no indent=no";

(:~
	Retrieves all the names of the fields in the Record
:)
declare function MedEx:get-sections() as text()*{
	
	let $sequence := collection(session:get-attribute('collection'))//Format/Section/name/text()
	return
		$sequence
};

(:~
	Retrieves all the inputs from the parameters
:)
declare function MedEx:read-input($format-name as xs:string*) as xs:string*
{
	for $x in $format-name return
		request:get-parameter(lower-case($x), ())
};

(:~
	Converts all the keywords to small case
:)
declare function MedEx:low-case($input as xs:string*) as xs:string*
{
	for $x in $input return
		fn:lower-case($x)
};

(:~
	Counts all the words in the input keywords
:)
declare function MedEx:count-words($input as xs:string*) as xs:int*
{
	for $x in $input
		let $count := fn:count(fn:tokenize($x, ", "))
		return $count
}; 

(:~
	Removes all the duplicate keywords in the sequence
:)
declare function MedEx:dist-words($input as xs:string*) as xs:string*
{
	for $x in $input
		let $distWords := fn:distinct-values(fn:tokenize($x, ", "))
		return fn:string-join($distWords, ", ")
};

(:~
	Removes all the stop words in the sequence of keywords
:)
declare function MedEx:remove-stopwords($input as xs:string*, $stopWords as xs:string*) as xs:string*
{
	for $x in $input return
		if(fn:boolean(fn:index-of($stopWords, $x))) then 
			""
		else
			$x
};

(:~
	Checks all the stop words in the sequence of keywords
:)
declare function MedEx:check-stopwords($dist-words as xs:string*, $stopwords as xs:string*) as xs:string*
{
	for $x in $dist-words
		let $input := MedEx:remove-stopwords(fn:tokenize($x, ", "), $stopwords)
		return $input
};

(:~
	The main function for filtering. This is where all the stop words are stored.
	This is also the function that will call the series of functions for filtering.
:)
declare function MedEx:get-keywords() as xs:string*
{
	let $format-name := MedEx:get-sections(),
		$stopwords := ("about", "all", "alone", "also", "am", "and", "as", "at", 
						"because", "before", "beside", "besides", "between", "but", 
						"by", "etc", "for", "i", "of", "on", "other", "others", "so", 
						"than", "that", "though", "to", "too", "trough", "until")
	
	let $input := MedEx:read-input($format-name)
	
	(:lower case:)
	let $input := MedEx:low-case($input)
	
	(:sequence word count per input:)
	let $word-count := MedEx:count-words($input)
	
	(:sequence of distinct words per input:)
	let $dist-words := MedEx:dist-words($input)
	
	(:remove stopwords:)
	let $clean-words := MedEx:check-stopwords($dist-words, $stopwords)
	
	return $clean-words
};

(:~
	Main function that will build the search query. It calls first the
	function for filtering. After filtering, it will generate the query
	that will be used to search the collection of the database. This is
	also where the configuration for strict and ordered query are done.
:)
declare function MedEx:query-results($stricted as xs:string?, $ordered as xs:string?) as element()*{
	
	(:let $keywords := ("fever", "cough", "sore throat", "stuffy nose", "headache", "muscle aches", "fatigue", "sweating", "Influenza", "Antibiotics") :)
	let $keywords := MedEx:get-keywords()
	let $order := $ordered
	let $strict := $stricted
	let $results := (
	
	let $query := if ($strict eq "false") then <query><bool>
						{for $item in distinct-values($keywords)
						return 
							<near occur = "should">{$item}</near>}
						</bool></query>
				else if ($ordered eq "true") then <query><bool><near ordered="yes">
						{for $item in distinct-values($keywords)
						return 
							<near occur = "must">{$item}</near>}
						</near></bool></query>
				else <query><bool>
						{for $item in distinct-values($keywords)
						return 
							<near occur = "must">{$item}</near>}
						</bool></query>
	for $record in collection(session:get-attribute('collection'))//RECORD[ft:query(., $query)]
		let $score := ft:score($record)
		order by $score descending
	return
		<RECORD>
			<SCORE>{$score}</SCORE>
			{$record/*}
		</RECORD>
        )
		return 
			let $null := request:set-attribute("results", $results)
			return $null
};


session:create(),
let $stricted := xs:string(request:get-parameter("stricted", ()))
let $ordered := xs:string(request:get-parameter("ordered", ()))
return
		MedEx:query-results($stricted, $ordered)